#include <string.h>
#include "M5ImgDec.h"
#include "ximage.h"

CM5ImgDec::CM5ImgDec()
{
	ximg = NULL ; 
}

CM5ImgDec::~CM5ImgDec()
{
	ResetImgDec() ; 
}

bool CM5ImgDec::ResetImgDec() {
	if(ximg) {
		ximg->SetEscape(1) ;
        delete ximg ;
        ximg = NULL ;  
    }
    return true ; 
}

void CM5ImgDec::StrLower(char * obj, const char * img_pathname)
{
	const char * a = img_pathname ; 
  	while (*a != '\0') {
	  *obj++ = (isupper(*a)) ? tolower (*a) : *a ;
	  ++a;
  	}
  	*obj = '\0' ; 
}

int CM5ImgDec::GetImgType(const char * img_pathname) {
	const char * file_exts[] = {
		TP_PNG, TP_GIF, TP_JPEG, 
		TP_TGA, TP_PCX, TP_WBMP, 
		TP_WMF, TP_ICO, TP_BMP
	} ; 
	
	int    ximg_type[] = {
		CXIMAGE_FORMAT_PNG, CXIMAGE_FORMAT_GIF, CXIMAGE_FORMAT_JPG, 
		CXIMAGE_FORMAT_TGA, CXIMAGE_FORMAT_PCX, CXIMAGE_FORMAT_WBMP,
		CXIMAGE_FORMAT_WMF, CXIMAGE_FORMAT_ICO, CXIMAGE_FORMAT_BMP
	} ; 
	
	int type_count = sizeof(file_exts) ; 
	char low_str[100] ;
	int i ; 
	
	// make sure 
	if(!img_pathname) return (int)(CXIMAGE_FORMAT_UNKNOWN) ; 
	
	// make pathname lower
	StrLower(low_str, img_pathname) ;  
	for(i = 0 ; i < type_count ; i++) {
		if(strstr(low_str, file_exts[i]) != (char *)(NULL)) {
			// find the image type, return 
			return ximg_type[i] ; 
		}
	}
	return (int)(CXIMAGE_FORMAT_UNKNOWN) ; 
}

bool CM5ImgDec::Decode(const char * img_pathname) 
{
	DWORD img_type = CXIMAGE_FORMAT_UNKNOWN ; 
	// find the image type by post fix of the file name 
	if(strlen(img_pathname)<= 0) return false ; 
	
	// make sure ximg is ready for decode 
	ResetImgDec() ; 
	
	// get image type through file name 
	img_type = GetImgType(img_pathname) ; 
	
	if(img_type != CXIMAGE_FORMAT_UNKNOWN) {
		// alloc a new CxImage object
		ximg = new CxImage() ; 
		
		// decode the specified image file 
		if(img_type != CXIMAGE_FORMAT_GIF) {
			// just to make sure the flag IS set 
			ximg->GetGIFAllFrames(false) ; 
		} else {
			// this will allow all the gif frames load at one time
			ximg->GetGIFAllFrames(true) ; 	
		}
		// load and decode the image 
		ximg->Load((char *)(img_pathname), img_type) ; 
		return true ; 
	}
	return false ; 
}

bool CM5ImgDec::Decode(u_char * buffer, u_short size, u_short imagetype) 
{	
	// make sure ximg is ready for decode 
	ResetImgDec() ; 
	
	if(imagetype != CXIMAGE_FORMAT_UNKNOWN) {
		// alloc a new CxImage object
		ximg = new CxImage(buffer, size, imagetype) ; 
		
		// decode the specified image file 
		if(imagetype != CXIMAGE_FORMAT_GIF) {
			// just to make sure the flag IS set 
			ximg->GetGIFAllFrames(false) ; 
		} else {
			// this will allow all the gif frames load at one time
			ximg->GetGIFAllFrames(true) ; 	
		}
		return true ; 
	}
	return false ; 
}

bool CM5ImgDec::Resize(int new_width, int new_height) 
{
	if(ximg) { 
		if(ximg->GetType() != CXIMAGE_FORMAT_GIF) {
			ximg->Resample(new_width, new_height) ; 			
		} else {
			for(int i = 0 ; i < ximg->GetFrame() ; i++) {
				(ximg->GetGIFFrame(i))->Resample(new_width, new_height) ; 
			}
		}
	}
	return true ; 
}

bool CM5ImgDec::RotateLeft() 
{
	if(ximg) {
		if(ximg->GetType() != CXIMAGE_FORMAT_GIF) {
			ximg->RotateLeft() ; 		
		} else {
			for(int i = 0 ; i < ximg->GetFrame() ; i++) {
				(ximg->GetGIFFrame(i))->RotateLeft() ;  
			}
		}
	}
	return true ; 
}

bool CM5ImgDec::RotateRright() 
{
	if(ximg) {
		if(ximg->GetType() != CXIMAGE_FORMAT_GIF) {
			ximg->RotateRight() ; 
		} else {
			for(int i = 0 ; i < ximg->GetFrame() ; i++) {
				(ximg->GetGIFFrame(i))->RotateRight() ; 
			}
		}
	}
	return true ; 
}

bool CM5ImgDec::Rotate180() 
{
	if(ximg) {
		if(ximg->GetType() != CXIMAGE_FORMAT_GIF) {
			ximg->Rotate180() ; 
		} else {
			for(int i = 0 ; i < ximg->GetFrame() ; i++) {
				(ximg->GetGIFFrame(i))->Rotate180() ; 
			}
		}	
	}
	return true ; 
}

bool CM5ImgDec::Mirror() 
{
	if(ximg) {
		if(ximg->GetType() != CXIMAGE_FORMAT_GIF) {
			ximg->Mirror() ; 
		} else {
			for(int i = 0 ; i < ximg->GetFrame() ; i++) {
				(ximg->GetGIFFrame(i))->Mirror() ; 
			}
		}
	}
	return true ; 
}

// rrrr0gggg00bbbb0
#define R_64K(r) (u_short)(((u_short)(r) & 0xf0) << 0x08)
#define G_64K(g) (u_short)(((u_short)(g) & 0xf0) << 0x03)
#define B_64K(b) (u_short)(((u_short)(b) & 0xf0) >> 0x03)
#define RGB_64K(r, g, b) (u_short)(R_64K(r) | G_64K(g) | B_64K(b))

bool CM5ImgDec::GetFrame(u_char * res_bmp, int frm_idx, int bmp_mode)
{
	int i = 0, j = 0, k = 0, l = 0; 
	int w = 0, h = 0 ; 
	int bpp = 0 ; 
	int src_pitch = 0 ; 
	int dst_pitch = 0 ; 
	int scan_jump = 0 ; 
	unsigned char  * src = NULL ; 
	unsigned short * dst = NULL ; 
	RGBQUAD rgb = {0, 0, 0, 0} ; 
	
	if(!ximg) return false ; 	
	
	// get source image data buffer 
	if(ximg->GetType() == CXIMAGE_FORMAT_GIF) {
		src = (ximg->GetGIFFrame(frm_idx))->GetBits() ; 	
	} else src = ximg->GetBits() ; 
	
	// get image info 
	w = ximg->GetWidth() ; 
	h = ximg->GetHeight() ; 
	bpp = ximg->GetBpp() ; 
		
	// four byte align pitch 
	if(bmp_mode == IMG_MOD_16BPP) {
		dst_pitch = w % 2 ; // 16k need 2 bytes align 
		if(bpp == 8) {
			src_pitch = (w % 4) ; // default is 4 bytes align 
			if(src_pitch) src_pitch = 4 - src_pitch ; 
			k = w * (h-1) + (h-1)*src_pitch ; 
			scan_jump = 2*w + src_pitch ; 
			dst = (unsigned short *)(res_bmp) ; 
			for(i = 0 ; i < h; i++) {
				for(j = 0 ; j < w ; j++) {
					rgb = ximg->GetPaletteColor(src[k++]) ; 
					dst[l++] = RGB_64K(rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue);  
				}
				k -= scan_jump ; 
				l += dst_pitch ; 
			}
		} else {
			src_pitch = (3 * w % 4) ; // 3 times of pitch 
			if(src_pitch) src_pitch = 4 - src_pitch ; 
			k = 3 * w * (h-1) + (h-1) * src_pitch ; 
			scan_jump = 2*3*w + src_pitch ; 
			dst = (unsigned short *)(res_bmp) ; 
			for(i = 0 ; i < h ; i++) {
				for(j = 0 ; j < w ; j++) {
					dst[l++] = RGB_64K(src[k+2], src[k+1], src[k]) ; 
					k += 3 ; 
				}
				k -= scan_jump ; 
				l += dst_pitch ; 
			}
		}
	} else return false ; 
	return true ; 
}

bool CM5ImgDec::GetFrame1(u_char * res_bmp, int frm_idx, int bmp_mode)
{
  	int i = 0, j = 0, k = 0, l = 0; 
	int w = 0, h = 0 ; 
	int bpp = 0 ; 
	int src_pitch = 0 ; 
	int dst_pitch = 0 ; 
	int scan_jump = 0 ; 
	unsigned char  * src = NULL ; 
	unsigned short * dst = NULL ; 
	RGBQUAD rgb = {0, 0, 0, 0} ; 
	
	if(!ximg) return false ; 	
	
	// get source image data buffer 
	if(ximg->GetType() == CXIMAGE_FORMAT_GIF) {
		src = (ximg->GetGIFFrame(frm_idx))->GetBits() ; 	
	} else src = ximg->GetBits() ; 
	
	// get image info 
	w = ximg->GetWidth() ; 
	h = ximg->GetHeight() ; 
	bpp = ximg->GetBpp() ; 
		
	// four byte align pitch 
	if(bmp_mode == IMG_MOD_16BPP) {
		dst_pitch = w % 2 ; // 16k need 2 bytes align 
		if(bpp == 8) {
			src_pitch = (w % 4) ; // default is 4 bytes align 
			if(src_pitch) src_pitch = 4 - src_pitch ; 
			k = w * (h-1) + (h-1)*src_pitch ; 
			scan_jump = 2*w + src_pitch ; 
			dst = (unsigned short *)(res_bmp) ; 
			for(i = 0 ; i < h; i++) {
				for(j = 0 ; j < w ; j++) {
					rgb = ximg->GetPaletteColor(src[k++]) ; 
					dst[l++] = RGB_64K(rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue);  
				}
				k -= scan_jump ; 
				l += dst_pitch ; 
			}
		} else {
			src_pitch = (3 * w % 4) ; // 3 times of pitch 
			if(src_pitch) src_pitch = 4 - src_pitch ; 
			k = 3 * w * (h-1) + (h-1) * src_pitch ; 
			scan_jump = 2*3*w + src_pitch ; 
			dst = (unsigned short *)(res_bmp) ; 
			for(i = 0 ; i < h ; i++) {
				for(j = 0 ; j < w ; j++) {
				  dst[l]  = (fast_div(src[k+2] * 0x1f) << 11) ; 
				  dst[l] |= (fast_div(src[k+1] * 0x3f) << 5) ; 
				  dst[l] |=  fast_div(src[k] * 0x1f) ; 
				  l++ ; 
				  k += 3 ; 
				}
				k -= scan_jump ; 
				l += dst_pitch ; 
			}
		}
	} else if(bmp_mode == IMG_MOD_24BPP) {
		dst_pitch = (w * 3) % 4 ; // 24k need 4 bytes align 
		if(bpp == 8) {
			src_pitch = (w % 4) ; // default is 4 bytes align 
			if(src_pitch) src_pitch = 4 - src_pitch ; 
			k = w * (h-1) + (h-1)*src_pitch ; 
			scan_jump = 2*w + src_pitch ; 
			dst = (unsigned short *)(res_bmp) ; 
			for(i = 0 ; i < h; i++) {
				for(j = 0 ; j < w ; j++) {
					rgb = ximg->GetPaletteColor(src[k++]) ; 
					dst[l++] = RGB_64K(rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue);  
				}
				k -= scan_jump ; 
				l += dst_pitch ; 
			}
		} else {
			src_pitch = (3 * w % 4) ; // 3 times of pitch 
			if(src_pitch) src_pitch = 4 - src_pitch ; 
			k = 3 * w * (h-1) + (h-1) * src_pitch ; 
			scan_jump = 2*3*w + src_pitch ; 
			dst = (unsigned short *)(res_bmp) ; 
#if 0
			k = (w * h + src_pitch) * 3; 
			memcpy(dst, src, k);
#else
			for(i = 0 ; i < h ; i++) {
				for(j = 0 ; j < w ; j++) {
					dst[l+2] = src[k+2];
					dst[l+1] = src[k+1];
					dst[l] = src[k];
					l += 3 ; 
					k += 3 ; 
				}
				k -= scan_jump ; 
				l += dst_pitch ; 
			}
#endif
		}
	} else return false ; 
	return true ; 
}

bool CM5ImgDec::GetFrameAlpha(u_char * res_bmp, int frm_idx, int bmp_mode) {
	unsigned char * src = NULL ; 
	unsigned char * dst = NULL ; 
	int dst_pitch = 0 ; 
	int src_pitch = 0 ; 
	int scan_jump = 0 ; 
	int w = 0, h = 0 ; 
	int i = 0 , j = 0 , k = 0 , l = 0 ; 
	BYTE alpha = 0 ; 
	
	if(!ximg || bmp_mode != IMG_MOD_GRAY) return false ; 
	
	w = ximg->GetWidth() ; 
	h = ximg->GetHeight() ; 
	
	// calc alpha or transparent color offset 
	if(ximg->AlphaIsValid()) src_pitch = 0 ; 
	else if(ximg->IsTransparent() || ximg->AlphaPaletteIsValid()) {
		src_pitch = (w % 4) ; // default is 4 bytes align 
		if(src_pitch) src_pitch = 4 - src_pitch ; 	
	}
		
	dst_pitch = (w % 4) ; 
	if(dst_pitch) dst_pitch = 4 - dst_pitch ; 
		
	k = w * (h-1) + (h-1)*src_pitch ; 
	scan_jump = 2*w + src_pitch ; 

	if(ximg->AlphaIsValid()) {
		if(ximg->GetType() != CXIMAGE_FORMAT_GIF) {
			// common image alpha pointer 
			src = (unsigned char *)(ximg->AlphaGetPointer(0, 0)) ; 	
		} else {
			// gif or multi-images alpha pointer 
			src = (unsigned char *)((ximg->GetGIFFrame(frm_idx))->AlphaGetPointer()) ; 
		}
		
		dst = (unsigned char *)(res_bmp) ; 
		for(i = 0 ; i < h ; i++ ) {
			for( j = 0 ; j < w ; j++) {
				dst[l++] = src[k++] ; 
			}
			k -= scan_jump ; 
			l += dst_pitch ; 
		}
	} else if(ximg->IsTransparent() || ximg->AlphaPaletteIsValid()) {
		RGBQUAD pixel_clr = {0, 0, 0, 0} ; 
		long    trans_idx = ximg->GetTransIndex() ; 
		long    pixel_idx = 0 ; 
		bool    is_palette_enable = ximg->AlphaPaletteIsEnabled() ; 

		if(ximg->GetType() != CXIMAGE_FORMAT_GIF) {
			// common image alpha pointer 
			src = (unsigned char *)(ximg->GetBits()) ; 	
		} else {
			// gif or multi-images
			src = (unsigned char *)((ximg->GetGIFFrame(frm_idx))->GetBits()) ; 
		}
		
		dst = (unsigned char *)(res_bmp) ; 
		// alpha palette enabled 
		for(i = 0; i < h ; i++) {
			for(j = 0 ; j < w ; j++) {
				alpha = 255 ; 
				pixel_idx = src[k++] ; 
				pixel_clr = ximg->GetPaletteColor((BYTE)(pixel_idx)) ; 
				
				// always false except for png-8 alpha 
				if(is_palette_enable)
					alpha = (BYTE)((alpha*(1+pixel_clr.rgbReserved))>>8);
				
				// pixel_idx != trans_idx used for gif alpha 
				// alpha != 0 for png-8 alpha
				if((pixel_idx != trans_idx) && (alpha != 0)) dst[l++] = 255 ; 
				else dst[l++] = 0 ; 
			}
			k -= scan_jump ; 
			l += dst_pitch ; 
		} 
	} else return false ; 
		return true ; 
}

int CM5ImgDec::GetWidth() {
	if(ximg) {
		if(ximg->GetType() != CXIMAGE_FORMAT_GIF)	{
			return ximg->GetWidth() ; 
		} else return (ximg->GetGIFFrame(0))->GetWidth() ; 
	}
	return -1 ; 
}

int CM5ImgDec::GetHeight() {
	if(ximg) {
		if(ximg->GetType() != CXIMAGE_FORMAT_GIF)	{
			return ximg->GetHeight() ; 
		} else return (ximg->GetGIFFrame(0))->GetHeight() ; 
	}
	return -1 ; 
}

int CM5ImgDec::GetFrameCount() 
{
	if(ximg && (ximg->GetType() == CXIMAGE_FORMAT_GIF)) {
		return ximg->GetNumFrames() ; 
	}
	return 1 ; 
}

bool CM5ImgDec::HasAlpha(int frm_idx) 
{
	if(ximg) {
		if(ximg->GetType() != CXIMAGE_FORMAT_GIF)	{
			return (ximg->AlphaIsValid() || 
					ximg->AlphaPaletteIsValid() ||
					ximg->IsTransparent()) ; 
		} else return ((ximg->GetGIFFrame(frm_idx))->AlphaIsValid() ||
					   (ximg->GetGIFFrame(frm_idx))->AlphaPaletteIsValid() ||
					   (ximg->GetGIFFrame(frm_idx))->IsTransparent()) ; 
	}
	return false ; 
}

bool CM5ImgDec::CreateMemBmp(int w, int h, int bpp) 
{
	ResetImgDec() ; 
	ximg = new CxImage(w, h, bpp, CXIMAGE_FORMAT_BMP) ; 
	return (ximg) ? true : false ; 
}

unsigned char * CM5ImgDec::GetBits()
{
	if(ximg) return ximg->GetBits() ; 
	return NULL ; 
}
 
const CxImage * CM5ImgDec::GetCxImage()
{
	return ximg ; 
}

bool CM5ImgDec::CopyMemBmp(CM5ImgDec& src)
{
	if(ximg) {
		ximg->Copy(*src.GetCxImage(), true, false, false) ; 
		return true ; 
	}
	return false ; 
}

bool CM5ImgDec::GetFrameInfo(M5FRAME_INFO * frm_info, int frm_idx)
{
	if(ximg->GetType() == CXIMAGE_FORMAT_GIF) { 
		CxImage* gif_frm = NULL ; 
		gif_frm = ximg->GetGIFFrame(frm_idx) ; 
		if(!gif_frm) return false ; 
		frm_info->delay = gif_frm->GetFrameDelay() * 10000 ; 
		frm_info->flag = gif_frm->GetFlags() ; 
		frm_info->whole_width = gif_frm->GetWidth() ; 
		frm_info->whole_height = gif_frm->GetHeight() ; 
		gif_frm->GetGIFFrameRect(&frm_info->frm_left, 
					 &frm_info->frm_top, 
					 &frm_info->frm_width, 
					 &frm_info->frm_height) ; 
		return true ; 
	}
	return false ; 
}
//end of file

